package edu.mse.camel.client.uml.collab.outlineview;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.LightweightSystem;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.Viewport;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.ui.parts.GraphicalEditor;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;

import edu.mse.camel.client.eventbus.pubsub.EventManager;
import edu.mse.camel.client.eventbus.pubsub.ISubscriber;
import edu.mse.camel.client.events.EventIDs;
import edu.mse.camel.client.events.IEvent;
import edu.mse.camel.client.events.peer.ViewportChangeEvent;
import edu.mse.camel.client.events.server.NotifyLoginStatusChange;
import edu.mse.camel.client.events.server.NotifySessionParticipantChange;
import edu.mse.camel.client.events.server.QueryLoginStatus;
import edu.mse.camel.client.events.util.Util;
import edu.mse.camel.client.session.team.QuerySessionParticipantStatus;
import edu.mse.camel.client.session.team.QuerySessionParticipantStatus.ParticipantInternal;
import edu.mse.camel.client.uml.collab.loginstate.SessionState;

import edu.mse.camel.client.log.Debug;
import edu.mse.camel.client.uml.collab.CollabPlugin;

public class PosterBoardThumbnailPage extends Page 
implements IContentOutlinePage, ISubscriber {

	private Map<String, ViewportFigure> viewports;

	private PosterBoardScrollableThumbnail thumbnail;

	private LightweightSystem lightweightSystem;

	private Control control;

	private EditPartViewer graphicalViewer;


	/**
	 * Constructs a ContentOutlinePage for the given viewer.
	 * 
	 * @param viewer the viewer for which a thumbnail should be created.
	 * @param selectionSynchronizer 
	 */
	public PosterBoardThumbnailPage(EditPartViewer viewer) {
		assert(viewer != null);
		this.graphicalViewer = viewer;
		viewports = new HashMap<String, ViewportFigure>();
		EventManager.getInstance().subscribeTo(
				EventIDs.VIEWPORT_CHANGED_EVENT, this);
		EventManager.getInstance().subscribeTo(
				EventIDs.NOTIFY_SESSION_PARTICIPANT_CHANGE_EVENT, this);
		EventManager.getInstance().subscribeTo(
				EventIDs.NOTIFY_LOGIN_STATUS_CHANGE_EVENT, this);
		
		QuerySessionParticipantStatus qspEvent = new QuerySessionParticipantStatus();
		EventManager.getInstance().dispatchEventSynchronous(qspEvent, true);
		if (qspEvent.isOpSuccessful()) {
			addOtherViewports(qspEvent);
		}
	}

	/**
	 * Do nothing, because this object doesn't fire selectionChange.
	 * 
	 * @param listener 
	 * 
	 * @see ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
	 */
	public void addSelectionChangedListener(ISelectionChangedListener listener) {

	}

	/**
	 * Forwards the createControl request to the editpartviewer.
	 * 
	 * @param parent 
	 * 
	 * @see org.eclipse.ui.part.IPage#createControl(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	public void createControl(Composite parent) {	

		// An swt canvas drawing the figure.
		Canvas canvas = new Canvas(parent, SWT.NONE);
		// A draw2d object linkink swt and draw2d
		lightweightSystem = new LightweightSystem(canvas);

		RootEditPart rep = getGraphicalViewer().getRootEditPart();
		if (rep instanceof ScalableFreeformRootEditPart) {
			ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart) rep;
			thumbnail = new PosterBoardScrollableThumbnail(
					(Viewport) root.getFigure(),
					getSelfColor());
			thumbnail.setBorder(new MarginBorder(3));
			thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
			thumbnail.setFigureColor(getSelfColor());
			lightweightSystem.setContents(thumbnail);
			for (ViewportFigure v : viewports.values()) {
				thumbnail.add(v);
			}
		}
		control = canvas;
	}

	private RGB getSelfColor() {
		if (SessionState.inSession && SessionState.myRGB != null) {
			return SessionState.myRGB;
		}
		return ColorConstants.menuBackgroundSelected.getRGB();
	}

	/**
	 * Dispose underlying objects.
	 */
	@Override
	public void dispose() {
		// deactivate the thumbnail
		thumbnail.deactivate();
		thumbnail = null;
		viewports.clear();
		super.dispose();
	}

	private EditPartViewer getGraphicalViewer() {
		return graphicalViewer;
	}

	public void changeGraphicalViewer(EditPartViewer viewer) {
		graphicalViewer = viewer;

		RootEditPart rep = viewer.getRootEditPart();
		if (rep instanceof ScalableFreeformRootEditPart) {
			ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart)rep;
			// To force revalidation
			thumbnail.setDirty(true);
			// set new figure and viewport
			thumbnail.setViewport((Viewport) root.getFigure());
			thumbnail.setSource(root.getLayer(LayerConstants.PRINTABLE_LAYERS));
		}
	}

	protected void setMyViewportColor(RGB rgb) {
		thumbnail.setFigureColor(rgb);
	}

	/**
	 * @param editor
	 */
	public void changeGraphicalEditor(GraphicalEditor editor) {
		GraphicalViewer viewer = (GraphicalViewer)editor.getAdapter(GraphicalViewer.class);
		changeGraphicalViewer(viewer);
	}

	@Override
	public Control getControl() {
		return control;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#getSelection()
	 */
	public ISelection getSelection() {
		return StructuredSelection.EMPTY;
	}


	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#removeSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
	 */
	public void removeSelectionChangedListener(ISelectionChangedListener listener) {

	}

	/**
	 * Sets focus to a part in the page.
	 */
	@Override
	public void setFocus() {
		if (getControl() != null) {
			getControl().setFocus();
		}
	}


	/* (non-Javadoc)
	 * @see org.eclipse.jface.viewers.ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)
	 */
	public void setSelection(ISelection selection) {
		if(selection instanceof IStructuredSelection) {
			IStructuredSelection sel = (IStructuredSelection)selection;
			Object editor = sel.getFirstElement();
			if( editor instanceof GraphicalEditor) {
				changeGraphicalEditor((GraphicalEditor)editor);
			}
		}
	}

	@Override
	public void notifyEvent(IEvent e) {
		if (e instanceof ViewportChangeEvent) {
			ViewportChangeEvent vc = (ViewportChangeEvent) e;
			final Rectangle rect = new Rectangle(vc.x, vc.y, vc.width, vc.height);
			final Figure f = viewports.get(vc.userID);
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {
					if(f != null) {
						f.setBounds(rect);
					}
				}

			});
		} else if (e instanceof NotifySessionParticipantChange) {
			NotifySessionParticipantChange sessionEvent = (NotifySessionParticipantChange) e;
			boolean foundFacilitator = false;
			SessionState.isFacilitator = false;

			//If I am not in session then set all defaults
			if (!sessionEvent.isInSession()) {
				SessionState.inSession = false;
				SessionState.myRGB = null;
				if (thumbnail != null) {
					thumbnail.setFigureColor(getSelfColor());
					removeOtherViewports();
				}
			} else {
				QuerySessionParticipantStatus qspEvent = new QuerySessionParticipantStatus();
				EventManager.getInstance().dispatchEventSynchronous(qspEvent, true);
				if (qspEvent.isOpSuccessful()) {
					addOtherViewports(qspEvent);
					
					for(QuerySessionParticipantStatus.ParticipantInternal 
							status : qspEvent.getParticipants()) {
						foundFacilitator = true;
						if(status.isFacilitator()) {
							if(SessionState.myUserID.equalsIgnoreCase(status.getId())) {
								SessionState.isFacilitator = true;
								break;
							}
						}
					}
				}
			}
			if(foundFacilitator == false) {
				Debug.log_warning(CollabPlugin.PLUGIN_ID, "PosterBoardThumbnailPage : Cannot find if I am facilitator or not!");
			}
			
		} else if (e instanceof NotifyLoginStatusChange) {
			NotifyLoginStatusChange loginEvent = (NotifyLoginStatusChange) e;
			if (loginEvent.isLoggedIn()) {
				SessionState.myUserID = loginEvent.getId();
			} else {
				SessionState.myUserID = null;
				SessionState.myRGB = null;
				if (thumbnail != null) {
					thumbnail.setFigureColor(getSelfColor());
				}
				removeOtherViewports();
			}
		}
	}

	private void removeOtherViewports() {
		for (final Figure f : viewports.values()) {
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {
					thumbnail.remove(f);
				}

			});
		}
		viewports.clear();
	}

	private void addOtherViewports(QuerySessionParticipantStatus qspEvent) {
		//The posterboard outline view may not be open when the user logged in
		//so we have to first check if we have logged in or not
		QueryLoginStatus loginEvent = new QueryLoginStatus();
		EventManager.getInstance().dispatchEventSynchronous(loginEvent, true);
		if (loginEvent.isOpSuccessful()) {
			if (loginEvent.isLoggedIn()) {
				SessionState.myUserID = loginEvent.getUserId();
			} else {
				return;
			}
		} else {
			return;
		}

		List<ParticipantInternal> participantList = qspEvent.getParticipants();

		//First clear all other viewports
		removeOtherViewports();

		//Then add them all again,
		//We don't expect people to log in and log out very often
		//so this approach of removing and adding viewports is reasonable
		//because it is easy to implement
		for (ParticipantInternal member : participantList) {
			String memberID = member.getId();
			java.awt.Color colorAWT = member.getColor(); 
			if (memberID.equals(SessionState.myUserID)) {
				SessionState.inSession = true;
				SessionState.myRGB = Util.getRGB(colorAWT);
				//Change my viewport figures color
				if (thumbnail != null) {
					thumbnail.setFigureColor(getSelfColor());
				}
				continue;//Don't add my viewport to the map
			}

			ViewportFigure vf = null;
			if(colorAWT != null) {
				RGB rgb = Util.getRGB(colorAWT);
				vf = new ViewportFigure(rgb);

			} else {//be default create a red viewport for others
				RGB rgb = Util.getRGB(ColorConstants.red);
				vf = new ViewportFigure(rgb);
			}

			if (viewports.get(memberID) == null) {
				viewports.put(memberID, vf);
			}

			//just so that we can pass to the ASyncExec
			final ViewportFigure f = viewports.get(memberID);
			Display.getDefault().asyncExec(new Runnable() {

				@Override
				public void run() {
					thumbnail.add(f);					
				}

			});
		}
	}
	
	
}
